import functools
import json
from datetime import datetime
from fastapi import status

import settings
from fastapi import exceptions
from log import logger


def cache_results(key: str, prefix: str = settings.REDIS_DB_PREFIX, seconds: int = settings.CACHE_EXPIRES):
    """
    :param key: Redis中存储的键（str类型)
    :param prefix: key的前缀，真正存储的是prefix_key
    :param seconds: 缓存时间，单位秒
    :return: 列表，元素为字典类型
    """
    key = prefix + '_' + key

    def inner(func):
        @functools.wraps(func)
        async def wrapper(request, *args, **kwargs):
            redis = request.app.state.redis
            try:
                results = await redis.get(key)
            except ConnectionRefusedError as e:  # redis中途崩了
                logger.error(str(e))
                raise exceptions.HTTPException(
                    status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=settings.SERVER_ERROR_MESSAGE)
            else:
                # 如果缓存中有数据，构造结果返回
                if results is not None:
                    return json.loads(results)

            results = await func(request, *args, **kwargs)
            if results is None:
                raise exceptions.HTTPException(status_code=status.HTTP_404_NOT_FOUND)

            # 解决日期时间不能JSON序列化-缓存不了的问题
            for result in results:
                if result.get('pub_date'):
                    result['pub_date'] = datetime.strftime(result['pub_date'], '%Y/%m/%d')
                if result.get('creation_time'):
                    result['creation_time'] = datetime.strftime(result['creation_time'], '%Y/%m/%d %H:%M:%S')
                if result.get('modification_time'):
                    result['modification_time'] = datetime.strftime(result['modification_time'], '%Y/%m/%d %H:%M:%S')

            try:
                # 把结果集放入redis中
                await redis.setex(key, seconds, json.dumps(results, ensure_ascii=False))
            except ConnectionRefusedError as e:  # redis中途崩了
                logger.error(str(e))
                raise exceptions.HTTPException(
                    status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=settings.SERVER_ERROR_MESSAGE)
            else:
                return results
        return wrapper
    return inner
